package jia.tools;

import java.util.Random;
import java.util.ArrayList;
import java.util.List;
import java.util.Arrays;
import jia.game.GenericFlags;
import org.apache.log4j.Logger;

/**
 * Series of static methods the simulate the rolling of various
 * dice during a character generation or during gameplay.  Each method
 * simulates a different type of dice, differentiated only by the number
 * of sides on the dice.  Each takes an argument of count, which tells
 * how many of the dice are to be rolled.<br>
 * <p/>
 * All methods use the Math.random method, while setting a Math.floor equal
 * to the number of sides for the "dice".  This ensures that the number
 * generated never goes over the number of sides being represented.  If the
 * user specifies more than one roll is to take place, the rolls are summed before
 * being returned by the method.  If the action that calls the method passes either
 * a 0 or a null for the number of rolls, the method will default to 1.
 *
 * @author Erik Stell
 * @version 1.0
 */
public class StaticDiceRoller {
    private static final Logger log = Logger.getLogger(StaticDiceRoller.class);
    
    /**
     * Static method that simulates an odd sided dice roll.  The count attribute
     * is to specify the number of dice to roll
     *
     * @param count - the number of dice to be rolled.
     * @return int value of the returned "roll", from 1 - the number specified
     */
    public static int rolldSomething(int sides, int count) {
        int retVal = 1;
        if (count <= 0) {
            count = 1;
        }
        try {
            for (int i = 0; i < count; i++) {
                //perform the random generation for the "roll"
                //Double tempVal = new Double(Math.floor(Math.random() * sides));
                //TODO:  Fix bug with duplicate numbers being called in rapid succession
                int tempVal = pickNumber(sides);
                Thread.sleep(5);
                
                //with each pass through the loop, add the values to the retVal variable.
                retVal = retVal + tempVal;
            }
            
            if (retVal > (sides * count)) {
                retVal = (sides * count);
            }
        } catch (Exception e) {
            if (GenericFlags.errorMessages){
                log.error(e.getMessage());
            }
        }
        
        return retVal;
    }
    
    /**
     * Static method that simulates an odd sided dice roll.  The count attribute
     * is to specify the number of dice to roll
     *
     * @param count - the number of dice to be rolled.
     * @return int value of the returned "roll", from 1 - the number specified
     */
    public static int rolldSomething(int sides, int count, boolean oldMethod) {
        int retVal = 1;
        if (count <= 0) {
            count = 1;
        }
        
        if (oldMethod){
            try {
                for (int i = 0; i < count; i++) {
                    //perform the random generation for the "roll"
                    Double tempVal = new Double(Math.floor(Math.random() * sides));
                    //TODO:  Fix bug with duplicate numbers being called in rapid succession
                    //int tempVal = pickNumber(sides);
                    //Thread.sleep(5);
                    
                    //with each pass through the loop, add the values to the retVal variable.
                    retVal = retVal + tempVal.intValue();
                    //retVal = retVal + tempVal;
                }
                
                if (retVal > (sides * count)) {
                    retVal = (sides * count);
                }
            } catch (Exception e) {
                if (GenericFlags.errorMessages){
                    log.error(e.getMessage());
                }
            }
        }else{
            try {
                for (int i = 0; i < count; i++) {
                    //perform the random generation for the "roll"
                    //Double tempVal = new Double(Math.floor(Math.random() * sides));
                    //TODO:  Fix bug with duplicate numbers being called in rapid succession
                    int tempVal = pickNumberInRange(1, sides);
                    Thread.sleep(5);
                    
                    //with each pass through the loop, add the values to the retVal variable.
                    //retVal = retVal + tempVal.intValue();
                    retVal = retVal + tempVal;
                }
                
                if (retVal > (sides * count)) {
                    retVal = (sides * count);
                }
            } catch (Exception e) {
                if (GenericFlags.errorMessages){
                    log.error(e.getMessage());
                }
            }
            
        }
        
        
        return retVal;
    }
    
    /**
     * Static method that simulates an odd sided dice roll.  The count attribute
     * is to specify the number of dice to roll
     *
     * @param count - the number of dice to be rolled.
     * @return int value of the returned "roll", from 1 - the number specified
     */
    public static int rolldStat(int sides, int count) {
        int retVal = 1;
        if (count <= 0) {
            count = 1;
        }
        try {
            if (count == 4){
                int[] mine = new int[4];
                mine[0]= rolldSomething(6, 1, true);
                mine[1]= rolldSomething(6, 1, false);
                mine[2]= rolldSomething(6, 1, true);
                mine[3]= rolldSomething(6, 1, false);
                
                
                Arrays.sort(mine);
                
                retVal = mine[1] + mine[2] + mine[3];
                Thread.sleep(15);
//
//                for (int i = 0; i < mine.length; i++) {
//                    //perform the random generation for the "roll"
//                    Double tempVal = new Double(Math.floor(Math.random() * sides));
//
//                    //with each pass through the loop, add the values to the retVal variable.
//                    retVal = retVal + tempVal.intValue();
//                }
//
//                if (retVal > (sides * count)) {
//                    retVal = (sides * count);
//                }
            }else{
                for (int i = 0; i < count; i++) {
                    //perform the random generation for the "roll"
                    Double tempVal = new Double(Math.floor(Math.random() * sides));
                    
                    //with each pass through the loop, add the values to the retVal variable.
                    retVal = retVal + tempVal.intValue();
                }
                
                if (retVal > (sides * count)) {
                    retVal = (sides * count);
                }
            }
            
        } catch (Exception e) {
            if (GenericFlags.errorMessages){
                log.error(e.getMessage());
            }
        }
        
        return retVal;
    }
    
    /**
     * Get a single random integer in the range 0 (inclusive) to N (exclusive).
     * <p/>
     * Important: if this method is called multiple times in rapid
     * succession, it will return the SAME result.
     *
     * @param aUpperLimit must be greater than 0.
     * @throws IllegalArgumentException if param does not comply.
     */
    private static int pickNumber(int aUpperLimit) {
        if (aUpperLimit <= 0) {
            throw new IllegalArgumentException("UpperLimit must be positive: " + aUpperLimit);
        }
        //if the no-argument constructor is used, the seed is taken as
        //the current time; thus, Random objects created in rapid succession
        //in this way will have the same seed, and will generate the same sequence
        //of random numbers.
        Random generator = new Random();
        return generator.nextInt(aUpperLimit);
    }
    
    /**
     * Generate a single random integer between aLowerLimit
     * and aUpperLimit, inclusive.
     * <p/>
     * Important: if this method is called twice in rapid succession, it will
     * return the same value.
     *
     * @throws IllegalArgumentException if aLowerLimit is not less
     *                                  than aUpperLimit.
     */
    private static int pickNumberInRange(int aLowerLimit, int aUpperLimit) {
        if (aLowerLimit >= aUpperLimit) {
            StringBuffer message = new StringBuffer();
            message.append("Lower limit (");
            message.append(aLowerLimit);
            message.append(") must be lower than Upper limit (");
            message.append(aUpperLimit);
            message.append(")");
            throw new IllegalArgumentException(message.toString());
        }
        
        Random generator = new Random();
        // get the range, casting to long to avoid overflow problems
        long range = (long) aUpperLimit - (long) aLowerLimit + 1;
        // compute a fraction of the range, 0 <= frac < range
        long fraction = (long) (range * generator.nextDouble());
        return (int) (fraction + aLowerLimit);
    }
    
    /**
     * Get a List of random Integers in the range 0 (inclusive) to N (exclusive).
     *
     * @param aUpperLimit must be greater than 0.
     * @param aListSize   must be greater than 0.
     * @throws IllegalArgumentException if param does not comply.
     */
    private List pickNumbers(int aUpperLimit, int aListSize) {
        if (aUpperLimit <= 0) {
            throw new IllegalArgumentException("UpperLimit must be positive: " + aUpperLimit);
        }
        if (aListSize <= 0) {
            throw new IllegalArgumentException("Size of returned List must be greater than 0.");
        }
        //reuse one and the same Random object to generate a series
        //of random Integers
        Random generator = new Random();
        List result = new ArrayList();
        for (int idx = 0; idx < aListSize; ++idx) {
            result.add(new Integer(generator.nextInt(aUpperLimit)));
        }
        return result;
    }
    
}
